/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package pe.edu.pucp.dp1.lectura.controlador;

import Shared.InterseccionSalida;
import java.io.File;
import java.util.ArrayList;
//import pe.edu.pucp.dp1.lectura.modelo.CoordCar;
//import pe.edu.pucp.dp1.lectura.modelo.FileCar;
//import pe.edu.pucp.dp1.lectura.modelo.ObjCar;
//import pe.edu.pucp.dp1.lectura.modelo.ListObjCarZona;

import Shared.*;
import java.sql.Time;
import pe.edu.pucp.dp1.procesamiento.controlador.*;
//import pe.edu.pucp.dp1.procesamiento.modelo.MatrizInterseccionSalida;
import Shared.MatrizInterseccionSalida;

/**
 *
 * @author Ed
 */
public class GestorCar {

    private static GestorCar instance;

    public static GestorCar getInstance() {
        if (instance == null) {
            instance = new GestorCar();
        }
        return instance;
    }

    //public static File ruta = new File("C:\\Users\\spsanchez\\Dropbox\\2014-1\\DP1\\datos2\\");//20 autos pcTrabajo
    //public static File ruta = new File("C:\\Users\\Ed\\Documents\\2014 - 1\\DP1\\datos\\datos\\");//200,000 autos pcPropia
    private File ruta = new File("C:\\Users\\Ed\\Dropbox\\2014-1\\DP1\\datos2\\");//20 autos pcPropia    

    private static ArrayList<FileCar> filelistcar = new ArrayList<>();//informacion de archivos
    public static ArrayList<ObjCar> listCarSimu = new ArrayList<>();//informacion de carros en simulacion(seg a seg se actualiza)
    private MatrizInterseccionSalida[][] semaforos;

    private int largoZona = 1000;
    private static int intervalHilo = 200;//significa que cada hilo trabajará 100 carros
    private String ipServProcesamiento = new String();

    private boolean hiloLanzado;

    //limites del hilo    
    public GestorCar() {
        setHiloLanzado(false);//para saber si el hilo fue lanzado y no ejecutarlo nuevamente
    }

    public void cargaArchivos() {
//        ArrayList<File> filesACargar = new ArrayList<>();
        for (File f : getRuta().listFiles()) {
            CharSequence cad = "RUT";
            if (f.getName().toUpperCase().contains(cad)) {
                FileCar carro = new FileCar(f);
                filelistcar.add(carro);
//                getListCarSimu().add(new ObjCar(f.getName()));
                getListCarSimu().add(new ObjCar(carro.getPlaca()));
//            System.out.println("autos Cargados:\t"+getFilelistcar().size());
//                filesACargar.add(f);
            }
            
//            ArrayList<HiloLectorFiles> listHilos = new ArrayList<>();
//            int totalCar = getListCarSimu().size();
//            // int hilosALanzar = (((int)(totalCar/getIntervalHilo())==0)) ? 1: (int)(totalCar/getIntervalHilo());
//            int hilosALanzar = totalCar / getIntervalHilo();
//            
//            for (int i = 0; i < hilosALanzar; i++) {
//                listHilos.add(new HiloLectorFiles(i,filesACargar));
//                listHilos.get(i).start();
            }            
         
    }

    public void leeRecorrido(int indiceCarro) {
        FileCar fileCarro = getFilelistcar().get(indiceCarro);
        CoordCar ptoPosicion = new CoordCar();
        //for(int i=0;i<50;i++)
        int i = 0;
        while (fileCarro.getSgtePto(false) != null) {
            ptoPosicion = fileCarro.getSgtePto(true);
            if (ptoPosicion == null) {
                System.out.println("No hay punto");
            } else {
                System.out.println("iteracion " + i + "\t[ " + ptoPosicion.getXx() + " , " + ptoPosicion.getYy() + " ]");
            }
            i++;
        }
    }

    //RECORRIDO DE OBJSIMULADOR
    public char sentidoTrayecto(int dx, int dy)// dx=x1-x0 dy=y1-y0
    {
        char sentRetorno = 'X';
        if (dx == 0 && dy == 00) {
//            setCountDetenido(getCountDetenido() + 1);
            int maxParado = 180;//maximo tiempo permitido como parado
//            if (getCountDetenido() >= maxParado) {
//                setAlarmStop(true);
//            }
        } else {
//            setCountDetenido(0);
//            setAlarmStop(false);
            if ((dx == 0) && (dy > 0)) {
                sentRetorno = 'N';
            } else if ((dx == 0) && (dy < 0)) {
                sentRetorno = 'S';
            } else if ((dx > 0) && (dy == 0)) {
                sentRetorno = 'E';
            } else if ((dx < 0) && (dy == 0)) {
                sentRetorno = 'O';
            } else if ((dx != 0) && (dy != 0)) {
                sentRetorno = 'X';
            }
        }
        return sentRetorno;
    }

    public boolean esInterseccion(int x, int y) {
        boolean esIntersec;
        if ((x % 100 == 0) && (y % 100 == 0)) {
            esIntersec = true;
        } else {
            esIntersec = false;
        }
        return esIntersec;
    }

    public int velocidadLimite(int x, int y, char sent) {
        int velo = 40;
        int xy;
        if ((sent == 'S') || (sent == 'N')) {
            xy = y;
        } else if ((sent == 'O') || (sent == 'E')) {
            xy = x;
        } else {
            return 0;
        }
        if ((xy % 500) == 0) {
            velo = 50;
            if ((xy % 2000) == 0) {
                velo = 60;
                if ((xy % 4000) == 0) {
                    velo = 70;
                }
            }
        }
        return velo;
    }

    public synchronized boolean avanzaInterseccion(int x, int y, char sentido, MatrizInterseccionSalida[][] semaforos) {
        boolean avanza = true;
        int zonaX = (int) (x / 1000);
        int zonaY = (int) (y / 1000);
        int intersecX = (int) ((x - zonaX * 1000) / 100);
        int intersecY = (int) ((y - zonaY * 1000) / 100);
//            System.out.println("Entré!!!");
        if (semaforos != null) {
            if (semaforos[zonaX][zonaY] != null) {
                InterseccionSalida intersec = semaforos[zonaX][zonaY].getMatrizInterseccionSalida()[intersecX][intersecY];
                if (intersec != null) {
//                            System.out.println("->intersec:\t"+"Estado"+intersec.getEstadoVerdeEjeX()+"Tiempo"+intersec.getTiempoRestante()+"\t("+x+","+y+")\t"+sentido);
//                    System.out.println("Entré!!!");
                    if (((((sentido == 'N') || (sentido == 'S'))) && (intersec.getEstadoVerdeEjeX() == 0))
                            || ((((sentido == 'O') || (sentido == 'E'))) && (intersec.getEstadoVerdeEjeX() == 1))) {
                        avanza = false;
//                        System.out.println("metodoAvanzaIntersec: " +"sentido: "+sentido +" - "+ "EstadoVerde: "+intersec.getEstadoVerdeEjeX() + "\tintersecAnalizada: (" + x+"," +  y+")");
                    }
                }
            }
        }
        //parte semaforos
        return avanza;
    }

    public CoordCar retornoPosicion(int x, int y, CoordCar sgtePto, char sentido, int metrosAvance, MatrizInterseccionSalida[][] semaforo, int distsSemf) {
        CoordCar retorno = null;
        int xyProxIntersec = 0;
        switch (sentido) {
            //de subida
            case 'N':
                xyProxIntersec = (y - (y % 100) + 100);
                if (((y + metrosAvance) >= (xyProxIntersec)) && (y < xyProxIntersec)) {
                    if (this.avanzaInterseccion(x, xyProxIntersec, sentido, semaforos)) {
                        retorno = new CoordCar(x, y + metrosAvance);
                    } else {
                        retorno = new CoordCar(x, xyProxIntersec - distsSemf);
                    }
                } else {
                    retorno = new CoordCar(x, y + metrosAvance);
                }                
                break;                
            //de bajada
            case 'S':
                xyProxIntersec = (y - (y % 100));
                if (((y - metrosAvance) <= (xyProxIntersec)) && (y > xyProxIntersec)) {
                    if (this.avanzaInterseccion(x, xyProxIntersec, sentido, semaforos)) {
                        retorno = new CoordCar(x, y - metrosAvance);
                    } else {
                        retorno = new CoordCar(x, xyProxIntersec + distsSemf);
                    }
                } else {
                    retorno = new CoordCar(x, y - metrosAvance);
                }
                break;
            //a la derecha
            case 'E':
                xyProxIntersec = (x - (x % 100) + 100);
                if (((x + metrosAvance) > (xyProxIntersec)) && (x < xyProxIntersec)) {
                    if (this.avanzaInterseccion(xyProxIntersec, y, sentido, semaforos)) {
                        retorno = new CoordCar(x + metrosAvance, y);
                    } else {
                        retorno = new CoordCar(xyProxIntersec - distsSemf, y);
                    }
                } else {
                    retorno = new CoordCar(x + metrosAvance, y);
                }
                break;
            //a la izquierda
            case 'O':
                xyProxIntersec = (x - (x % 100));
                if (((x - metrosAvance) <(xyProxIntersec)) && (x > xyProxIntersec)) {
                    if (this.avanzaInterseccion(xyProxIntersec, y, sentido, semaforos)) {
                        retorno = new CoordCar(x - metrosAvance, y);
                    } else {
                        retorno = new CoordCar(xyProxIntersec + distsSemf, y);
                    }
                } else {
                    retorno = new CoordCar(x - metrosAvance, y);
                }
                break;
            default:
                retorno = new CoordCar(x, y);
                break;
        }
//        System.out.println("ptoActual: ("+x+" , "+y+") sentido: "+sentido+"\tpinta IntersecProx: "+ xyProxIntersec+" retorno: ("+retorno.getXx()+","+
//        retorno.getYy()+")");
        return retorno;
    }

    public CoordCar nuevaPosicion(CoordCar actual, CoordCar posibleSgte, char sentido, int maxMetrosAvanzar, MatrizInterseccionSalida[][] semaforos) {//aqui se utiliza la infor de choke choke
        CoordCar nuevoPto = new CoordCar();
        int distSemf = 1;
        int newY, newX;
        int newYposible, newXposible;
        switch (sentido) {
            case 'N':
                newY = actual.getYy() + maxMetrosAvanzar;
                newYposible = posibleSgte.getYy();
                nuevoPto.setXx(actual.getXx());
                if (newY < newYposible) {
//                    if (this.esInterseccion(nuevoPto.getXx(), newY)) {
//                        newY = (this.avanzaInterseccion(actual.getXx(), newY, sentido, semaforos)) ? newY : (newY - distSemf);
//                    }
//                    else{
                    CoordCar ptoCar = this.retornoPosicion(actual.getXx(), actual.getYy(), posibleSgte, sentido, maxMetrosAvanzar, semaforos, distSemf);
                    newY = (ptoCar == null) ? newY : ptoCar.getYy();
//                    }
                    nuevoPto.setYy(newY);
                } else {
                    newYposible = (this.avanzaInterseccion(actual.getXx(), newYposible, sentido, semaforos)) ? newYposible : (newYposible - distSemf);
                    nuevoPto.setYy(newYposible);
                }
                break;
            case 'S':
                newY = actual.getYy() - maxMetrosAvanzar;
                newYposible = posibleSgte.getYy();
                nuevoPto.setXx(actual.getXx());
                if (newY > newYposible) {
//                    if (this.esInterseccion(nuevoPto.getXx(), newY)) {
//                        newY = (this.avanzaInterseccion(actual.getXx(), newY, sentido, semaforos)) ? newY : (newY + distSemf);
//                    }
//                    else{
                    CoordCar ptoCar = this.retornoPosicion(actual.getXx(), actual.getYy(), posibleSgte, sentido, maxMetrosAvanzar, semaforos, distSemf);
                    newY = (ptoCar == null) ? newY : ptoCar.getYy();
//                    }
                    nuevoPto.setYy(newY);
                } else {
                    newYposible = (this.avanzaInterseccion(actual.getXx(), newYposible, sentido, semaforos)) ? newYposible : (newYposible + distSemf);
                    nuevoPto.setYy(newYposible);
                }
                break;
            case 'E':
                newX = actual.getXx() + maxMetrosAvanzar;
                newXposible = posibleSgte.getXx();
                nuevoPto.setYy(actual.getYy());
                if (newX < newXposible) {
//                    if (this.esInterseccion(newX, nuevoPto.getYy())) {
//                        newX = (this.avanzaInterseccion(newX, actual.getYy(), sentido, semaforos)) ? newX : (newX - distSemf);
//                    }
//                    else{
                    CoordCar ptoCar = this.retornoPosicion(actual.getXx(), actual.getYy(), posibleSgte, sentido, maxMetrosAvanzar, semaforos, distSemf);
                    newX = (ptoCar == null) ? newX : ptoCar.getXx();
//                    }
                    nuevoPto.setXx(newX);
                } else {
                    newXposible = (this.avanzaInterseccion(newXposible, actual.getYy(), sentido, semaforos)) ? newXposible : (newXposible - distSemf);
                    nuevoPto.setXx(newXposible);
                }
                break;
            case 'O':
                newX = actual.getXx() + maxMetrosAvanzar;
                newXposible = posibleSgte.getXx();
                nuevoPto.setYy(actual.getYy());
                if (newX > newXposible) {
//                    if (this.esInterseccion(newX, nuevoPto.getYy())) {
//                        newX = (this.avanzaInterseccion(newX, actual.getYy(), sentido, semaforos)) ? newX : (newX + distSemf);
//                    }
//                    else{
                    CoordCar ptoCar = this.retornoPosicion(actual.getXx(), actual.getYy(), posibleSgte, sentido, maxMetrosAvanzar, semaforos, distSemf);
                    newX = (ptoCar == null) ? newX : ptoCar.getXx();
//                    }
                    nuevoPto.setXx(newX);
                } else {
                    newXposible = (this.avanzaInterseccion(newXposible, actual.getYy(), sentido, semaforos)) ? newXposible : (newXposible + distSemf);
                    nuevoPto.setXx(newXposible);
                }
                break;
//            case 'X':
//                nuevoPto.setXx(posibleSgte.getXx());
//                nuevoPto.setYy(posibleSgte.getYy());
//                break;
        }
//        System.out.println("Ptos felices:\t("+nuevoPto.getXx()+","+nuevoPto.getYy());
        return nuevoPto;
    }

    public boolean avanzaPunto(ObjCar objCar, FileCar fileCarro, MatrizInterseccionSalida[][] semaforos)//retorna falso si no existen puntos para avanzar
    {
        boolean avanzo;
        CoordCar posActual = objCar.getPosAct();
        CoordCar pseudoSgte = fileCarro.getSgtePto(false);
        if (pseudoSgte == null)//no hay mas puntos q recorrer
        {
//            System.out.println("No hay mas puntos ");
            avanzo = false;
            objCar.setSentido('X');
            objCar.setVelocidad(0);
        } else {
            if ((posActual == null) && (objCar.getPosX() == -1) && (objCar.getPosY() == -1))//primer movimiento del carro        
            {
                fileCarro.getSgtePto(true);
                objCar.setPosAct(pseudoSgte);
                objCar.setSentido('X');
                objCar.setVelocidad(0);
                objCar.setVelocidad(velocidadLimite(objCar.getPosAct().getXx(), objCar.getPosAct().getYy(), objCar.getSentido()));
            } else {
                int dx = pseudoSgte.getXx() - objCar.getPosAct().getXx();
                int dy = pseudoSgte.getYy() - objCar.getPosAct().getYy();
                char pseudoSentido = sentidoTrayecto(dx, dy);
                int velLim = this.velocidadLimite(objCar.getPosAct().getXx(), objCar.getPosAct().getYy(), pseudoSentido);
                int metrosEnSegundo = (velLim * 10 / 36);// factor de conversion de km/h a m/s                
                CoordCar nuevoPto = nuevaPosicion(objCar.getPosAct(), pseudoSgte, pseudoSentido, metrosEnSegundo, semaforos);
                objCar.setVelocidad(velLim);
                if ((pseudoSgte.getXx() == nuevoPto.getXx()) && (pseudoSgte.getYy() == nuevoPto.getYy()))//nuevo punto es el mismo q el sgte punto del archivo
                {
                    fileCarro.getSgtePto(true);
//                    System.out.print("Entre y pase\t");
                }
                objCar.setPosAct(nuevoPto);
                objCar.setSentido(pseudoSentido);
//                System.out.println("metAvanzaPto-ptoActual: ("+objCar.posAct.getXx()+" , "+objCar.posAct.getYy()+") sentido: "+objCar.sentido+
//                        "\tpinta IntersecProx: ("+pseudoSgte.getXx() +","+pseudoSgte.getYy());
            }
            avanzo = true;
        }
        return avanzo;
    }
    // RECORRIDO DE OBJSIMULADOR
    
    
    public void lanzaHiloSimulador() {
        if (!isHiloLanzado()) {
            ArrayList<HiloUpdateCar> listHilos = new ArrayList<>();
            int totalCar = getFilelistcar().size();
            // int hilosALanzar = (((int)(totalCar/getIntervalHilo())==0)) ? 1: (int)(totalCar/getIntervalHilo());
            int hilosALanzar = totalCar / getIntervalHilo();
            setHiloLanzado(true);
            (new HiloUpdateSemaforo()).start();
            for (int i = 0; i < hilosALanzar; i++) {
                listHilos.add(new HiloUpdateCar(i));
                listHilos.get(i).start();
            }
        }
    }

    public void lanzaHiloMiron() {
        HiloVisualizarCar hiloMiron = new HiloVisualizarCar();
        hiloMiron.start();
    }

    public double velocidadPromedio()//GUI//RMI
    {
        double velAcumulado = 0;
        int cantVehiculos = 0;
        for (int i = 0; i < getListCarSimu().size(); i++) {
            if (getListCarSimu().get(i).getCountDetenido() < 180) {
                velAcumulado += getListCarSimu().get(i).getVelocidad();
                cantVehiculos++;
            }
        }
        return (velAcumulado / cantVehiculos);
    }

    public ListObjCarZona listCarrosPorZona(int x, int y)//RMI
    {
        //(x,y) posicion inicial de la zona de la cual se extraera la lista de autos        
        ArrayList<ObjCar> listCarRetorno = new ArrayList<>();
        int largeZona = getLargoZona();
        for (int i = 0; i < getListCarSimu().size(); i++) {
            ObjCar carSimu = getListCarSimu().get(i);
            if (carSimu.getCountDetenido() < 180 && carSimu.getPosX() != -1 && carSimu.getPosY() != -1)//si fuese mas de 180 seg ya no se considera
            {
                int xxactual = carSimu.getPosX();
                int yyactual = carSimu.getPosY();
                if (x <= xxactual && (x + largeZona - 1) >= xxactual && y <= yyactual && (y + largeZona - 1) >= yyactual) {
                    listCarRetorno.add(carSimu);
                }
            }
        }
        ListObjCarZona objRetorno = new ListObjCarZona(x, y, listCarRetorno);
        if (objRetorno.getListCars().size() > 0) {
//            System.out.println("objRetorno: "+x+" "+y+" "+objRetorno.getListCars().size());
        }

        return objRetorno;
    }

    public ArrayList<ListObjCarZona> listCarrosTotal()//RMI
    {
        ArrayList<ListObjCarZona> objRetorno = new ArrayList<>();
        int xMax = 240000;
        int yMax = 160000;
        int largeZona = getLargoZona();
        for (int xx = 0; xx < xMax; xx += largeZona) {
            for (int yy = 0; yy < yMax; yy += largeZona) {
                ListObjCarZona listZona = this.listCarrosPorZona(xx, yy);
                if (listZona.getListCars().size() > 0) {
                    objRetorno.add(listZona);
                }
            }
        }
        return objRetorno;
    }

    public void iniLectura() {
        MainLectura.main(null);
    }

    //SELECTORES
    public ArrayList<FileCar> getFilelistcar() {
        return filelistcar;
    }

    private void setFilelistcar(ArrayList<FileCar> aListcar) {
        filelistcar = aListcar;
    }

    public boolean isHiloLanzado() {
        return hiloLanzado;
    }

    private void setHiloLanzado(boolean hiloLanzado) {
        this.hiloLanzado = hiloLanzado;
    }

    public ArrayList<ObjCar> getListCarSimu() {
        return listCarSimu;
    }

    private void setListCarSimu(ArrayList<ObjCar> aListCarSimu) {
        listCarSimu = aListCarSimu;
    }

    public int getLargoZona() {
        return largoZona;
    }

    public void setLargoZona(int largoZona) {
        this.largoZona = largoZona;
    }

    public int getIntervalHilo() {
        return intervalHilo;
    }

    private void setIntervalHilo(int aIntervalHilo) {
        intervalHilo = aIntervalHilo;
    }

    public String getIpServProcesamiento() {
        return ipServProcesamiento;
    }

    public void setIpServProcesamiento(String ipServProcesamiento) {
        this.ipServProcesamiento = ipServProcesamiento;
    }

    public File getRuta() {
        return ruta;
    }

    public void setRuta(File aRuta) {
        ruta = new File(aRuta.getAbsolutePath());
    }

    public MatrizInterseccionSalida[][] getSemaforos() {
        return semaforos;
    }

    public void setSemaforos(MatrizInterseccionSalida[][] semaforos) {
        this.semaforos = semaforos;
    }
}
